package client;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Arrays;

import javax.swing.JFrame;

import chat.ChatPacket;

import protocol.*;
import gui.*;
import response.*;
import structures.CtfObject;
import structures.Maze;
import structures.Position;
import update.*;


/**
 * A test client that uses CtfPackets to send and receive data.
 * 
 * @author WHOMP
 */
public class Main {
	static Socket client;
	static CtfDataOutputStream cdos;
	static CtfDataInputStream cdis;
	public static boolean isLoggingIn = true;
	public static InetAddress ipAddress;
	public static String username;

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		//connectAndSendPacket();
		//
		
		
		// create Frame
		JFrame jf = new JFrame("Capture The Stars!");
		
		CtfLogin ctflogin = new CtfLogin("Login Screen", jf);
		
		while ( isLoggingIn ) {
			Thread.yield();
		}
		
		try {
			ipAddress = InetAddress.getByName(CtfLogin.ip.getText());
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		username = CtfLogin.user.getText();
		
		ctflogin.closeGui();
		
		
		
/** BEGIN CTS **/
		connect();
		
		ConnectBody cb = sendReceiveConnect();
		System.out.println( "connect response: " + cb.getIdInGame() );
		
		Receiver receiveThread = new Receiver();
		receiveThread.initSocket(cdis);
		receiveThread.start();
		
//		// trigger updates
//		cdos.send( new CtfPacket( CtfState.getMyPlayer().getId(), MsgTypes.CELL_UPDATE, CtfState.getUpdateState() ));
		
		/*
		MoveBody mb = sendReceiveMove();

		System.out.println( "move response: " + mb.getPosition().toString() );
		
		PickupBody pb = sendReceivePickup();
		System.out.println( "pickup response: " + pb.getObjectId() );
		
		DropBody db = sendReceiveDrop();
		System.out.println( "drop response: " + db.getObjectId() + db.getPosition().toString() );
		
		BlastBody bb = sendReceiveBlast();
		System.out.println( "blast response: " + bb.getPosition().toString() );
		
		DisconnectBody dc = sendReceiveDisconnect();
		System.out.println( "disconnect response: " + dc.getResult() );

		// trigger updates
		cdos.send( new CtfPacket( State.getMyPlayer().getId(), MsgTypes.CELL_UPDATE, State.getUpdateState() ));
		
		CellUpdatePacket cup = receiveCellUpdatePacket();
		System.out.println( "cell update: " + cup.getNumCellUpdates() );
		
		PlayerUpdatePacket pup = receivePlayerUpdatePacket();
		System.out.println( "player update: " + pup.getNumPlayerUpdates() );
		
		GameUpdateBody gup = receiveGameUpdate();
		System.out.println( "game update: " + gup.getGameState() );
		*/
		CtfGui gui = new CtfGui("ProtocolTestClient", jf);
		gui.repaint();
	}
	
	/**
	 * Chat it up.
	 * @param msg
	 */
	public static void sendChatMsg( String msg ) {
		msg = "["+Main.username + "] : " +  msg;
		ChatPacket cp = new ChatPacket( CtfState.getMyPlayer().getId(), msg);
		CtfPacket p = new CtfPacket( CtfState.getMyPlayer().getId(), MsgTypes.CHAT_MSG, 
				CtfState.getGameState(), cp.toNetworkByteArray() );
		cdos.send(p);
	}
	/**
	 * Send a packet to test server receive()
	 */
	public static void connectAndSendPacket() {
		try {
			// set up client
			client = new Socket( "curzon.bu.edu", 2222 );
			System.out.println("Connected.");
			
			// client I/O streams
			cdos = new CtfDataOutputStream( client.getOutputStream() );
			cdos.flush();
			cdis = new CtfDataInputStream( client.getInputStream() );
			
			CtfPacket p = new CtfPacket( 4, MsgTypes.MAP, 0 );
			cdos.send(p);
			
		} catch ( IOException e ) {
			e.printStackTrace();
		}
	}
	
	public static ConnectBody sendReceiveConnect() {
		// send and receive connect packet
		cdos.send( new CtfPacket( GameStateTypes.UNASSIGNED_CLIENT, 
					MsgTypes.CONNECT, GameStateTypes.NOT_YET_STARTED ));
		
		// map transmission
		receiveMapPacketCreateMaze();
		
		CtfPacket p = cdis.receive();
		try {
			// instantiate State
			ConnectBody cb = new ConnectBody( p.getBody() );
			return cb;
		} catch (CtfActionException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	
	public static MoveBody sendReceiveMove() {
		// send and receive move packet
		MoveBody req = new MoveBody( new Position(7,7) );
		cdos.send( new CtfPacket( CtfState.getMyPlayer().getId(), MsgTypes.MOVE, 
					CtfState.getUpdateState(), req.toNetworkByteArray() ));
		CtfPacket p = cdis.receive();
		try {
			MoveBody mb = new MoveBody( p.getBody() );
			// if not 0,0, update myPlayer
			CtfState.getMyPlayer().setPos(mb.getPosition());
			return mb;
		} catch (CtfActionException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static MoveBody sendReceiveMove(MoveBody req) {
		// send and receive move packet
	//	MoveBody req = new MoveBody( new Position(7,7) );
	//	System.out.println("")
	//	System.out.println("send receive move " + State.getMyPlayer().getPos().toString());
		cdos.send( new CtfPacket( CtfState.getMyPlayer().getId(), MsgTypes.MOVE, 
					CtfState.getUpdateState(), req.toNetworkByteArray() ));
		CtfPacket p = cdis.receive();
		try {
			MoveBody mb = new MoveBody( p.getBody() );
			// if not 0,0, update myPlayer
			if ( !mb.getPosition().isInvalid() )
				CtfState.getMyPlayer().setPos(mb.getPosition());
			System.out.println( "move response: " + mb.getPosition().toString() );
			return mb;
		} catch (CtfActionException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static void sendMove(MoveBody req) {
		cdos.send( new CtfPacket( CtfState.getMyPlayer().getId(), MsgTypes.MOVE, 
				CtfState.getUpdateState(), req.toNetworkByteArray() ));
	}
	
	public static PickupBody sendReceivePickup() {
		// send and receive pickup packet
		cdos.send( new CtfPacket( CtfState.getMyPlayer().getId(), MsgTypes.PICKUP, CtfState.getUpdateState() ));
		CtfPacket p = cdis.receive();
		try {
			PickupBody pb = new PickupBody( p.getBody() );
			return pb;
		} catch (CtfActionException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static DropBody sendReceiveDrop() {
		// send and receive drop packet
		cdos.send( new CtfPacket( CtfState.getMyPlayer().getId(), MsgTypes.DROP, CtfState.getUpdateState() ));
		CtfPacket p = cdis.receive();
		try {
			DropBody db = new DropBody( p.getBody() );
			return db;
		} catch (CtfActionException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static BlastBody sendReceiveBlast() {
		// send and receive blast packet
		cdos.send( new CtfPacket( CtfState.getMyPlayer().getId(), MsgTypes.BLAST, 
					CtfState.getUpdateState() ));
		CtfPacket p = cdis.receive();
		try {
			BlastBody bb = new BlastBody( p.getBody() );
			return bb;
		} catch (CtfActionException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static DisconnectBody sendReceiveDisconnect() {
		// send and receive disconnect packet
		cdos.send( new CtfPacket( CtfState.getMyPlayer().getId(), MsgTypes.DISCONNECT, 
					CtfState.getUpdateState() ));
		CtfPacket p = cdis.receive();
		try {
			DisconnectBody dc = new DisconnectBody( p.getBody() );
			return dc;
		} catch (CtfActionException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * Updates
	 */
	// cell update or player position update
	public static CellUpdatePacket receiveCellUpdatePacket() {
		CtfPacket r = cdis.receive();
		try {
			CellUpdatePacket cup = new CellUpdatePacket( r.getBody() );
			/* update update state */
			CtfState.setUpdateState( r.getHeader().getUpdateState() );
			return cup;
		} catch (CtfActionException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * Extract data from the cell update bodies
	 * @param cup
	 */
	public static void processCellUpdateBodies( CellUpdatePacket cup ) {
		byte[] cellUpdates = cup.getCellUpdatesInBytes();
		// for each cell update
		for( int i = 0; i < cup.getNumCellUpdates(); i++ ) {
			byte[] cellUpdate = Arrays.copyOfRange(cellUpdates, 
					i*CellUpdateBody.LENGTH, (i+1)*CellUpdateBody.LENGTH);
			try {
				CellUpdateBody cub = new CellUpdateBody( cellUpdate );
				//TODO
				
				// set Maze object position, if any
				int objId = cub.getOccupiedByObject();
				switch ( objId ) {
					case CtfObject.TEAM1_FLAG_ID:
						Maze.setFlag1Pos( new Position(cub.getRow(), cub.getCol())); 
						break;
					case CtfObject.TEAM1_BOMBS_ID:
						Maze.setBomb1Pos( new Position(cub.getRow(), cub.getCol())); 
						break;
					case CtfObject.TEAM2_FLAG_ID:
						Maze.setFlag2Pos( new Position(cub.getRow(), cub.getCol())); 
						break;
					case CtfObject.TEAM2_BOMBS_ID:
						Maze.setBomb2Pos( new Position(cub.getRow(), cub.getCol())); 
						break;
				}
					
				
			} catch (CtfActionException e) {
				e.printStackTrace();
			}
		}
	}
	
	// player update
	public static PlayerUpdatePacket receivePlayerUpdatePacket() {
		CtfPacket r = cdis.receive();
		try {
			PlayerUpdatePacket pup = new PlayerUpdatePacket( r.getBody() );
			/* update update state */
			CtfState.setUpdateState( r.getHeader().getUpdateState() );
			return pup;
		} catch (CtfActionException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * Extract data from the player update bodies
	 * @param cup
	 */
	public static void processPlayerUpdateBodies( PlayerUpdatePacket pup ) {
		byte[] playerUpdates = pup.getPlayerUpdatesInBytes();
		// for each cell update
		for( int i = 0; i < pup.getNumPlayerUpdates(); i++ ) {
			byte[] playerUpdate = Arrays.copyOfRange(playerUpdates, 
					i*PlayerUpdateBody.LENGTH, (i+1)*PlayerUpdateBody.LENGTH);
			try {
				PlayerUpdateBody pub = new PlayerUpdateBody( playerUpdate );
				//TODO
			} catch (CtfActionException e) {
				e.printStackTrace();
			}
		}
	}
	
	// game update
	public static GameUpdateBody receiveGameUpdate() {
		CtfPacket r = cdis.receive();
		
		GameUpdateBody gup = new GameUpdateBody( r.getBody() );
		
		/*********** Game Start **********************/
		// make sure that cell updates have been received prior to this
		CtfState.setGameState( GameStateTypes.STARTED );
		/* update update state */
		CtfState.setUpdateState( r.getHeader().getUpdateState() );
		
		return gup;
	}
	
	/**
	 * Receive a map packet and create a maze structure from the map.
	 */
	public static MapPacket receiveMapPacketCreateMaze() {
		CtfPacket r = cdis.receive();
		
		System.out.println( r.getBody().length );
		
		MapPacket mp = new MapPacket( r.getBody() );
		
		//System.out.println( mp.getHeader().toString() );
		//System.out.println( mp.getMap() );
		return mp;
	}
	

	/**
	 * Blueprint for connecting to the ProtocolTestServer and 
	 * establishing input/output streams.
	 */
	public static void connect() {
		try {
			// set up client
			client = new Socket( ipAddress, 2222 );
			System.out.println("Connected.");
			
			// client I/O streams
			cdos = new CtfDataOutputStream( client.getOutputStream() );
			cdos.flush();
			cdis = new CtfDataInputStream( client.getInputStream() );
		
		} catch ( IOException e ) {
//			e.printStackTrace();
			System.out.println("The server is not accepting new connections at this time. Please try again later!");
		}
	}
}
	
